
import dumper
import re
import operator
import pickle
import numpy

from TarjanMap import TarjanMap
from TarjanDirection import Directions

class TarjanController(object):

    wall_types = [ '', 'wall', 'door', 'locked_door', 'gate', 'arch', 'secret' ]
    floor_types = [ '', 'empty', 'darkness', 'stairs_up', 'stairs_down' ]

    def __init__(self, view, map):
        self.view = view
        self.map = map
        self.view.map = map

        map.attach(view)
        view.attach(self)

        self.rel_pos = None
        self.view.cursor_direction = Directions.north
        self.view.cursor_pos = (self.map.width / 2, self.map.height / 2)

        view.update(0, 0, map.width, map.height)

        self.command_table = {
            '<UP>': (self.moveInDirection, Directions.north),
            '<LEFT>': (self.moveInDirection, Directions.west),
            '<DOWN>': (self.moveInDirection, Directions.south),
            '<RIGHT>': (self.moveInDirection, Directions.east),
            '<SPACE>': (self.switchMode, 'switched'),
            '<SHIFT-UP>': (self.changeWall, Directions.north),
            '<SHIFT-LEFT>': (self.changeWall, Directions.west),
            '<SHIFT-DOWN>': (self.changeWall, Directions.south),
            '<SHIFT-RIGHT>': (self.changeWall, Directions.east),
            '<SHIFT-CTRL-UP>': (self.moveMap, Directions.north),
            '<SHIFT-CTRL-LEFT>': (self.moveMap, Directions.west),
            '<SHIFT-CTRL-DOWN>': (self.moveMap, Directions.south),
            '<SHIFT-CTRL-RIGHT>': (self.moveMap, Directions.east),
            '<CTRL-UP>': (self.changeFloor, ),
            '<CTRL-DOWN>': (self.changeFloor, False),
            '<<(\w+)>>': (self.dispatch, )
        }

        self.switched_mode = {
            '<UP>': (self.moveForward, ),
            '<LEFT>': (self.turnLeft, ),
            '<DOWN>': (self.moveBackward, ),
            '<RIGHT>': (self.turnRight, ),
            '<SHIFT-UP>': (lambda self: self.changeWall(self.view.cursor_direction), self),
            '<SHIFT-LEFT>': (lambda self: self.changeWall(self.view.cursor_direction - 1), self),
            '<SHIFT-DOWN>': (lambda self: self.changeWall(self.view.cursor_direction, False), self),
            '<SHIFT-RIGHT>': (lambda self: self.changeWall(self.view.cursor_direction + 1), self)
        }

        self.mark_mode = {
            '<UP>': (self.extendMarkNorth, ),
            '<LEFT>': (self.extendMarkWest, ),
            '<DOWN>': (self.extendMarkSouth, ),
            '<RIGHT>': (self.extendMarkEast, ),
            '<SHIFT-UP>': (self.moveMarkNorth, ),
            '<SHIFT-LEFT>': (self.moveMarkWest, ),
            '<SHIFT-DOWN>': (self.moveMarkSouth, ),
            '<SHIFT-RIGHT>': (self.moveMarkEast, ),
        }

    def switchMode(self, modeName):
        "switch between compass and relative movement mode"
        old_mode = {}
        mode = getattr(self, modeName + "_mode")
        for key in mode.keys():
            old_mode[key] = self.command_table.pop(key)

        self.command_table.update(mode)
        setattr(self, modeName + "_mode", old_mode)

        self.view.update(*self.view.cursor_pos)

    def update(self, event, *args):
        "send event through command table, and dispatch to function"
        for cmd, action in self.command_table.items():
            matches = re.match(cmd, event)
            if matches:
                arguments = list(action[1:])
                arguments.extend(matches.groups())
                arguments.extend(args)
                action[0](*arguments)
                break

    def dispatch(self, cmd, *args):
        "call self[cmd] with *args"
        getattr(self, cmd)(*args)

    def moveInDirection(self, dir):
        "move current position in view in direction"
        move = { Directions.north:(0,-1), Directions.south:(0,1), Directions.east:(1,0), Directions.west:(-1,0) }
        self.moveTo( map(operator.add, move[dir], self.view.cursor_pos) )

    def moveForward(self):
        self.moveInDirection(self.view.cursor_direction)

    def moveBackward(self):
        self.moveInDirection(~self.view.cursor_direction)

    def turnLeft(self):
        self.view.cursor_direction = self.view.cursor_direction - 1
        self.view.update(*self.view.cursor_pos)

    def turnRight(self):
        self.view.cursor_direction = self.view.cursor_direction + 1
        self.view.update(*self.view.cursor_pos)

    def moveTo(self, pos):
        "set current position in view to pos, if pos is inside map"
        if 0 <= pos[0] < self.map.width and 0 <= pos[1] < self.map.height:
            old = self.view.cursor_pos
            self.view.cursor_pos = tuple(pos)
            self.view.update(*old)
            self.view.update(*pos)
            return True
        else:
            return False

    def changeWall(self, dir, forward_in_list=True):
        cell = self.map.getCell(*self.view.cursor_pos)
        idx = self.wall_types.index(cell.getWall(dir)) + (1 if forward_in_list else -1)
        cell.setWall(dir, self.wall_types[idx % len(self.wall_types)])

    def changeFloor(self, forward_in_list=True):
        cell = self.map.getCell(*self.view.cursor_pos)
        idx = self.floor_types.index(cell.floor) + (1 if forward_in_list else -1)
        cell.floor = self.floor_types[idx % len(self.floor_types)]

    def changeMap(self, new_map):
        if self.map:
            self.map.detach(self.view)
            if new_map.width < self.map.width or new_map.height < self.map.height:
                self.view.cursor_pos = new_map.width / 2, new_map.height / 2
        self.map = new_map
        self.view.map = self.map
        self.map.attach(self.view)
        self.view.update(0, 0, self.map.width, self.map.height)

    def NewMap(self, width, height):
        "create a new map, and set view to display it"
        self.changeMap(TarjanMap(width, height))

    def SetNote(self, note):
        self.map.getCell(*self.view.cursor_pos).notes = note

    def ResizeMap(self, width, height):
        w = width < self.map.width and width or self.map.width
        h = height < self.map.height and height or self.map.height
        new_map = TarjanMap(width, height)
        new_map.setMap( self.map.getMap(0, 0, w, h), 0, 0)
        self.changeMap(new_map)

    def SaveMap(self, filename):
        "Save current map to filename"
        data = [self.view.text, self.map]
        self.map.detach(self.view)
        self.map.tainted = False
        file = open(filename, 'w')
        pickle.dump(data, file)
        file.close()
        self.map.attach(self.view)
        self.view.update(0,0)

    def OpenMap(self, filename):
        "Open map from filename"
        file = open(filename, 'r')
        data = pickle.load(file)
        file.close()
        #self.view.set_text(data[0])
        self.view.text = data[0]
        self.changeMap(data[1])

    def GoTo(self, coords):
        if coords[0] < 0:
            coords = (self.width  - coords[0], coords[1])
        if coords[1] < 0:
            coords = (coords[0], self.width - coords[1])
        self.moveTo(coords)

    def moveMap(self, dir):
        w = self.map.width
        h = self.map.height
        new_map = TarjanMap(w, h)
        if (dir == Directions.north):
            new_map.setMap(self.map.getMap(0, 1, w, h - 1), 0, 0)
        if (dir == Directions.east):
            new_map.setMap(self.map.getMap(0, 0, w - 1, h), 1, 0)
        if (dir == Directions.south):
            new_map.setMap(self.map.getMap(0, 0, w, h - 1), 0, 1)
        if (dir == Directions.west):
            new_map.setMap(self.map.getMap(1, 0, w - 1, h), 0, 0)
        self.changeMap(new_map)

    def EnterMarkMode(self):
        self.switchMode("mark")
        if self.view.marked_area is not None:
            zone = self.view.marked_area 
            self.view.marked_area = None
            self.map.notify(*zone)
        else:
            self.view.marked_area = self.view.cursor_pos + (1,1)
            self.map.notify(*self.view.marked_area)

    def extendMarkNorth(self): self.extend_marked_area( ( 0,  0,  0,  -1) )
    def extendMarkWest(self):  self.extend_marked_area( ( 0,  0, -1,  0) )
    def extendMarkSouth(self): self.extend_marked_area( ( 0,  0,  0,  1) )
    def extendMarkEast(self):  self.extend_marked_area( ( 0,  0,  1,  0) )
    def moveMarkNorth(self):   self.move_marked_area( ( 0, -1, 0, 0) )
    def moveMarkWest(self):    self.move_marked_area( (-1,  0, 0, 0) )
    def moveMarkSouth(self):   self.move_marked_area( ( 0,  1, 0, 0) )
    def moveMarkEast(self):    self.move_marked_area( ( 1,  0, 0, 0) )

    def extend_marked_area(self, matrix):
        self.view.marked_area = numpy.add( self.view.marked_area, matrix )
        self.map.notify(*self.view.marked_area)

    def move_marked_area(self, matrix):
        buffer = self.map.getMap(*self.view.marked_area)
        self.map.clearMap(*self.view.marked_area)
        self.view.marked_area = numpy.add(self.view.marked_area, matrix )
        self.map.setMap(buffer, *self.view.marked_area[:2])



